//
// Created by WWR on 2025/7/27.
//

#include "W9825G6.h"
#include "st_log.h"

#define TAG "SDRAM"

/******************************************************************************************************
*	函 数 名: SDRAM_Initialization_Sequence
*	入口参数: hsdram - SDRAM_HandleTypeDef定义的变量，即表示定义的sdram
*				 Command	- 控制指令
*	返 回 值: 无
*	函数功能: SDRAM 参数配置
*	说    明: 配置SDRAM相关时序和控制方式
*******************************************************************************************************/
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
{
    __IO uint32_t tmpmrd = 0;

    /* Configure a clock configuration enable command */
    Command->CommandMode 				= FMC_SDRAM_CMD_CLK_ENABLE;	// 开启SDRAM时钟
    Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK; 	// 选择要控制的区域
    Command->AutoRefreshNumber 		= 1;
    Command->ModeRegisterDefinition 	= 0;

    HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);	// 发送控制指令
    HAL_Delay(1);		// 延时等待

    /* Configure a PALL (precharge all) command */
    Command->CommandMode 				= FMC_SDRAM_CMD_PALL;		// 预充电命令
    Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK;	// 选择要控制的区域
    Command->AutoRefreshNumber 		= 1;
    Command->ModeRegisterDefinition 	= 0;

    HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);  // 发送控制指令

    /* Configure a Auto-Refresh command */
    Command->CommandMode 				= FMC_SDRAM_CMD_AUTOREFRESH_MODE;	// 使用自动刷新
    Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK;          // 选择要控制的区域
    Command->AutoRefreshNumber			= 8;                                // 自动刷新次数
    Command->ModeRegisterDefinition 	= 0;

    HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);	// 发送控制指令

    /* Program the external memory mode register */
    tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2         |
             SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
             SDRAM_MODEREG_CAS_LATENCY_3           |
             SDRAM_MODEREG_OPERATING_MODE_STANDARD |
             SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

    Command->CommandMode					= FMC_SDRAM_CMD_LOAD_MODE;	// 加载模式寄存器命令
    Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK;	// 选择要控制的区域
    Command->AutoRefreshNumber 		= 1;
    Command->ModeRegisterDefinition 	= tmpmrd;

    HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);	// 发送控制指令

    HAL_SDRAM_ProgramRefreshRate(hsdram, 918);  // 配置刷新率
}

/******************************************************************************************************
*	函 数 名: SDRAM_Test
*	入口参数: 无
*	返 回 值: SUCCESS - 成功，ERROR - 失败
*	函数功能: SDRAM测试
*	说    明: 先以16位的数据宽度写入数据，再读取出来一一进行比较，随后以8位的数据宽度写入，
*				 用以验证NBL0和NBL1两个引脚的连接是否正常。
*******************************************************************************************************/

uint8_t SDRAM_Test(void)
{
    uint32_t i = 0;			// 计数变量
    uint16_t ReadData = 0; 	// 读取到的数据
    uint8_t  ReadData_8b;

    uint32_t ExecutionTime_Begin;		// 开始时间
    uint32_t ExecutionTime_End;		// 结束时间
    uint32_t ExecutionTime;				// 执行时间
    float    ExecutionSpeed;			// 执行速度
    ST_LOG(LOG_I,TAG,">------------------------------------------SDRAM-----------------------------------------------<");
    ST_LOG(LOG_I,TAG,"Conduct a speed test for the SDRAM\r\n");         // 进行SDRAM速度测试

// 写入 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    ExecutionTime_Begin 	= HAL_GetTick();	// 获取 systick 当前时间，单位ms

    for (i = 0; i < SDRAM_Size/2; i++)
    {
        *(__IO uint16_t*) (SDRAM_BANK_ADDR + 2*i) = (uint16_t)i;		// 写入数据
    }
    ExecutionTime_End		= HAL_GetTick();											// 获取 systick 当前时间，单位ms
    ExecutionTime  = ExecutionTime_End - ExecutionTime_Begin; 				// 计算擦除时间，单位ms
    ExecutionSpeed = (float)SDRAM_Size /1024/1024 /ExecutionTime*1000 ; 	// 计算速度，单位 MB/S

    ST_LOG(LOG_I,TAG,"Write data with a 16-bit data width,Size: %d MB,Time: %d ms,write spend:%.2f MB/s",SDRAM_Size/1024/1024,ExecutionTime,ExecutionSpeed);

// 读取	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    ExecutionTime_Begin 	= HAL_GetTick();	// 获取 systick 当前时间，单位ms

    for(i = 0; i < SDRAM_Size/2;i++ )
    {
        ReadData = *(__IO uint16_t*)(SDRAM_BANK_ADDR + 2 * i );  // 从SDRAM读出数据
    }
    ExecutionTime_End		= HAL_GetTick();											// 获取 systick 当前时间，单位ms
    ExecutionTime  = ExecutionTime_End - ExecutionTime_Begin; 				// 计算擦除时间，单位ms
    ExecutionSpeed = (float)SDRAM_Size /1024/1024 /ExecutionTime*1000 ; 	// 计算速度，单位 MB/S

    ST_LOG(LOG_I,TAG,"Data reading is complete,Size: %d MB,Time: %d ms,read spend:%.2f MB/s\r\n",SDRAM_Size/1024/1024,ExecutionTime,ExecutionSpeed);

// 数据校验 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    ST_LOG(LOG_I,TAG,"Conducting data verification>>>");         // 进行SDRAM数据校验

    for(i = 0; i < SDRAM_Size/2;i++ )
    {
        ReadData = *(__IO uint16_t*)(SDRAM_BANK_ADDR + 2 * i );  // 从SDRAM读出数据
        if( ReadData != (uint16_t)i )      //检测数据，若不相等，跳出函数,返回检测失败结果。
        {
            ST_LOG(LOG_E,TAG,"SDRAM data verification failed!!!");         // SDRAM数据校验失败
            return ERROR;	 // 返回失败标志
        }
    }

    /*16位数据宽度读写通过，以8位数据宽度写入数据*/
    ST_LOG(LOG_I,TAG,"It reads and writes with a data width of 16 bits, and writes data with a data width of 8 bits");
    for (i = 0; i < 255; i++)
    {
        *(__IO uint8_t*) (SDRAM_BANK_ADDR + i) =  (uint8_t)i;
    }
    ST_LOG(LOG_I,TAG,"Writing is complete, reading data and comparing...");    // 写入完毕，读取数据并比较...
    for (i = 0; i < 255; i++)
    {
        ReadData_8b = *(__IO uint8_t*) (SDRAM_BANK_ADDR + i);
        if( ReadData_8b != (uint8_t)i )      //检测数据，若不相等，跳出函数,返回检测失败结果。
        {
            ST_LOG(LOG_E,TAG,"The read and write test with 8-bit data width has failed!!");         // 8位数据宽度读写测试失败
            ST_LOG(LOG_E,TAG,"Please check the connection of NBL0 and NBL1");         // 请检查NBL0和NBL1的连接
            return ERROR;	 // 返回失败标志
        }
    }
    ST_LOG(LOG_I,TAG,"8 bits of data width can be read and written successfully");  //8位数据宽度读写通过
    ST_LOG(LOG_I,TAG,"The SDRAM read and write test has passed, and the system is working normally");   //SDRAM读写测试通过，系统正常
    ST_LOG(LOG_I,TAG,">----------------------------------------------------------------------------------------------<");
    return SUCCESS;	 // 返回成功标志
}